import std/strutils

type
  X1bCsiCommand* = object
    args*: seq[int] = @[]
    bonus*: string = "" # the ? in \e[?47h
    fun*: char = 0.chr
  X1bCommandKind* = enum
    csi, direct, c
  X1bCommand* = object
    kind*: X1bCommandKind
    csi*: X1bCsiCommand = X1bCsiCommand()
    direct*: char = 0.chr
    c*: char = 0.chr

proc x1bParse*(str: string): seq[X1bCommand] =
  var
    arg: int = 0
    state: int = 0
    command: X1bCommand
  for c in str:
    if state == 0:
      if c == '\x1b':
        state = 1
      else:
        result.add X1bCommand(kind: c, c: c)
    elif state == 1:
      if c == '[':
        command = X1bCommand(kind: csi)
        arg = 0
        state = 2
      else:
        result.add X1bCommand(kind: direct, direct: c)
        state = 0
    elif state == 2:
      if c.isDigit():
        arg = c.int - '0'.int
        state = 3
      elif c == ';':
        command.csi.args.add 0
        state = 3
      else:
        command.csi.bonus = command.csi.bonus & $c
    elif state == 3:
      if c.isDigit():
        arg *= 10
        arg += c.int - '0'.int
      elif c == ';':
        command.csi.args.add arg
        arg = 0
      else:
        command.csi.args.add arg
        command.csi.fun = c
        result.add command
        state = 0
